using System;
using System.Globalization;
using dnAnalytics.Math;
using NUnit.Framework;

namespace dnAnalytics.Tests.Math
{
    [TestFixture]
    public class Complex32Test
    {
        [Test]
        public void AbsoluteTest()
        {
            Complex32 a = new Complex32(4, -2);
            Assert.AreEqual(4.47213602f, a.Absolute);
        }

        [Test]
        public void AddTest()
        {
            Complex32 a = new Complex32(3, -3);
            Complex32 b = new Complex32(-1, 1);
            Assert.AreEqual(new Complex32(2, -2), a.Add(b));
        }

        [Test]
        public void ConjugateTest()
        {
            Complex32 a = new Complex32(4, -2);
            Assert.AreEqual(new Complex32(4, 2), a.Conjugate);
        }

        [Test]
        public void DivideTest()
        {
            Complex32 a = new Complex32(4, -4);
            Complex32 b = new Complex32(-2, 2);
            Assert.AreEqual(new Complex32(-2, 0), a.Divide(b));
        }

        [Test]
        public void EqualsTest()
        {
            Complex32 z1 = new Complex32(-1.1f, 2.2f);
            Complex32 z2 = new Complex32(-1.1f, 2.2f);
            Assert.IsTrue(z1 == z2);
            Assert.IsTrue(z1.Equals(z2));
        }

        [Test]
        [ExpectedException(typeof (FormatException))]
        public void FormatExceptionTest1()
        {
            new Complex32("");
        }

        [Test]
        [ExpectedException(typeof (FormatException))]
        public void FormatExceptionTest2()
        {
            new Complex32("+");
        }

        [Test]
        [ExpectedException(typeof (FormatException))]
        public void FormatExceptionTest3()
        {
            new Complex32("1i+2");
        }

        [Test]
        public void GetInfinityTest()
        {
            Assert.AreEqual(new Complex32(float.PositiveInfinity, float.PositiveInfinity), Complex32.Infinity);
        }

        [Test]
        public void GetNaNTest()
        {
            Assert.AreEqual(float.NaN, Complex32.NaN.Real);
            Assert.AreEqual(float.NaN, Complex32.NaN.Imaginary);
        }

        [Test]
        public void GetOneTest()
        {
            Assert.AreEqual(new Complex32(1), Complex32.One);
        }

        [Test]
        public void GetZeroTest()
        {
            Assert.AreEqual(new Complex32(0, 0), Complex32.Zero);
        }

        [Test]
        public void HashTest()
        {
            Complex32 z = new Complex32(1.1f, 2.2f);
            Assert.AreEqual(z.GetHashCode(), 11);
        }

        [Test]
        public void ImplicitConversionTest()
        {
            Complex32 a = new Complex32(1, -2);
            Complex b = a;
            Assert.AreEqual(b.Real, a.Real);
            Assert.AreEqual(b.Imaginary, a.Imaginary);
        }

        [Test]
        public void InequalityTest()
        {
            Complex32 a = 1;
            Complex32 b = 2;
            Assert.IsTrue(a != b);
        }

        [Test]
        public void InfinityTest()
        {
            Complex32 z = new Complex32(Single.NegativeInfinity, 1.1f);
            Assert.IsTrue(Complex32.IsInfinity(z));
            z = new Complex32(1.1f, Single.NegativeInfinity);
            Assert.IsTrue(Complex32.IsInfinity(z));
            z = new Complex32(Single.PositiveInfinity, 1.1f);
            Assert.IsTrue(Complex32.IsInfinity(z));
            z = new Complex32(1.1f, Single.PositiveInfinity);
            Assert.IsTrue(Complex32.IsInfinity(z));
            z = new Complex32(1.1f, 2.2f);
            Assert.IsFalse(Complex32.IsInfinity(z));
        }

        [Test]
        public void MultiplyTest()
        {
            Complex32 a = new Complex32(4, -4);
            Complex32 b = new Complex32(-2, 2);
            Assert.AreEqual(new Complex32(0, 16), a.Multiply(b));
        }

        [Test]
        public void NaNTest()
        {
            Complex32 z = new Complex32(Single.NaN, 1.1f);
            Assert.IsTrue(Complex32.IsNaN(z));
            z = new Complex32(1.1f, Single.NaN);
            Assert.IsTrue(Complex32.IsNaN(z));
            z = new Complex32(1.1f, 2.2f);
            Assert.IsFalse(Complex32.IsNaN(z));
        }

        [Test]
        [ExpectedException(typeof (ArgumentNullException))]
        public void NullString()
        {
            new Complex32(null);
        }

        [Test]
        public void ParseTest()
        {
            string s = "1";
            Complex32 z = new Complex32(s);
            Assert.AreEqual(z.Real, 1);
            Assert.AreEqual(z.Imaginary, 0);

            s = "i";
            z = new Complex32(s);
            Assert.AreEqual(z.Real, 0);
            Assert.AreEqual(z.Imaginary, 1);

            s = "2i";
            z = new Complex32(s);
            Assert.AreEqual(z.Real, 0);
            Assert.AreEqual(z.Imaginary, 2);

            s = "1 + 2i";
            z = new Complex32(s);
            Assert.AreEqual(z.Real, 1);
            Assert.AreEqual(z.Imaginary, 2);

            s = "1+2i";
            z = new Complex32(s);
            Assert.AreEqual(z.Real, 1);
            Assert.AreEqual(z.Imaginary, 2);

            s = "1 - 2i";
            z = new Complex32(s);
            Assert.AreEqual(z.Real, 1);
            Assert.AreEqual(z.Imaginary, -2);

            s = "1-2i";
            z = new Complex32(s);
            Assert.AreEqual(z.Real, 1);
            Assert.AreEqual(z.Imaginary, -2);

            s = "1 - 2i";
            z = new Complex32(s);
            Assert.AreEqual(z.Real, 1);
            Assert.AreEqual(z.Imaginary, -2);

            s = "1,2";
            z = new Complex32(s);
            Assert.AreEqual(z.Real, 1);
            Assert.AreEqual(z.Imaginary, 2);

            s = "1 , 2 ";
            z = new Complex32(s);
            Assert.AreEqual(z.Real, 1);
            Assert.AreEqual(z.Imaginary, 2);

            s = "1,2i";
            z = new Complex32(s);
            Assert.AreEqual(z.Real, 1);
            Assert.AreEqual(z.Imaginary, 2);

            s = "-1, -2i";
            z = new Complex32(s);
            Assert.AreEqual(z.Real, -1);
            Assert.AreEqual(z.Imaginary, -2);

            s = "(+1,2i)";
            z = new Complex32(s);
            Assert.AreEqual(z.Real, 1);
            Assert.AreEqual(z.Imaginary, 2);

            s = "(-1 , -2)";
            z = new Complex32(s);
            Assert.AreEqual(z.Real, -1);
            Assert.AreEqual(z.Imaginary, -2);

            s = "(-1 , -2i)";
            z = new Complex32(s);
            Assert.AreEqual(z.Real, -1);
            Assert.AreEqual(z.Imaginary, -2);

            s = "(+1e1 , -2e-2i)";
            z = new Complex32(s);
            Assert.AreEqual(z.Real, 10);
            Assert.AreEqual(z.Imaginary, -.02, 7);

            s = "1 + 2i";
            z = Complex32.Parse(s);
            Assert.AreEqual(z.Real, 1);
            Assert.AreEqual(z.Imaginary, 2);
        }

        [Test]
        public void PlusTest()
        {
            Complex32 a = new Complex32(1, 1);
            Assert.AreEqual(new Complex32(1, 1), Complex32.Plus(a));
        }

        [Test]
        public void SubtractTest()
        {
            Complex32 a = new Complex32(3, -3);
            Complex32 b = new Complex32(-1, 1);
            Assert.AreEqual(new Complex32(4, -4), a.Subtract(b));
        }

        [Test]
        public void ToComplexTest()
        {
            Assert.AreEqual(new Complex32(2), Complex32.ToComplex32(2));
        }

        [Test]
        public void ToStringTest()
        {
            Complex32 a = new Complex32(1, -2);
            Assert.AreEqual("1 -2i", a.ToString());
        }

        [Test]
        public void TryParseFormatExceptionTest1()
        {
            Complex32 z;
            bool ret = Complex32.TryParse("", out z);
            Assert.IsFalse(ret);
            Assert.AreEqual(0, z.Real);
            Assert.AreEqual(0, z.Imaginary);
        }

        [Test]
        public void TryParseFormatExceptionTest2()
        {
            Complex32 z;
            bool ret = Complex32.TryParse("+", out z);
            Assert.IsFalse(ret);
            Assert.AreEqual(0, z.Real);
            Assert.AreEqual(0, z.Imaginary);
        }

        [Test]
        public void TryParseFormatExceptionTest3()
        {
            Complex32 z;
            bool ret = Complex32.TryParse("1i+2", out z);
            Assert.IsFalse(ret);
            Assert.AreEqual(0, z.Real);
            Assert.AreEqual(0, z.Imaginary);
        }

        [Test]
        public void TryParseNullString()
        {
            Complex32 z;
            bool ret = Complex32.TryParse(null, out z);
            Assert.IsFalse(ret);
            Assert.AreEqual(0, z.Real);
            Assert.AreEqual(0, z.Imaginary);
        }

        [Test]
        public void TryParseTest()
        {
            Complex32 z;
            bool ret = Complex32.TryParse("1", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(1, z.Real);
            Assert.AreEqual(0, z.Imaginary);

            ret = Complex32.TryParse("-1", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(-1, z.Real);
            Assert.AreEqual(0, z.Imaginary);

            ret = Complex32.TryParse("-i", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(0, z.Real);
            Assert.AreEqual(-1, z.Imaginary);

            ret = Complex32.TryParse("i", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(0, z.Real);
            Assert.AreEqual(1, z.Imaginary);

            NumberFormatInfo ni = new NumberFormatInfo();
            ret = Complex32.TryParse(ni.NegativeInfinitySymbol + "," + ni.PositiveInfinitySymbol, out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(float.NegativeInfinity, z.Real);
            Assert.AreEqual(float.PositiveInfinity, z.Imaginary);

            ret = Complex32.TryParse(ni.NaNSymbol + "," + ni.NaNSymbol, out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(float.NaN, z.Real);
            Assert.AreEqual(float.NaN, z.Imaginary);

            ret = Complex32.TryParse(ni.NegativeInfinitySymbol + "+" + ni.PositiveInfinitySymbol + "i", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(float.NegativeInfinity, z.Real);
            Assert.AreEqual(float.PositiveInfinity, z.Imaginary);

            ret = Complex32.TryParse(ni.NaNSymbol + "+" + ni.NaNSymbol + "i", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(float.NaN, z.Real);
            Assert.AreEqual(float.NaN, z.Imaginary);

            ret = Complex32.TryParse(float.MaxValue.ToString("R") + " " + float.MinValue.ToString("R") + "i", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(float.MaxValue, z.Real);
            Assert.AreEqual(float.MinValue, z.Imaginary);

            ret = Complex32.TryParse("2i", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(0, z.Real);
            Assert.AreEqual(2, z.Imaginary);

            ret = Complex32.TryParse("1 + 2i", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(1, z.Real);
            Assert.AreEqual(2, z.Imaginary);

            ret = Complex32.TryParse("1+2i", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(1, z.Real);
            Assert.AreEqual(2, z.Imaginary);

            ret = Complex32.TryParse("1 - 2i", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(1, z.Real);
            Assert.AreEqual(-2, z.Imaginary);

            ret = Complex32.TryParse("1-2i", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(1, z.Real);
            Assert.AreEqual(-2, z.Imaginary);

            ret = Complex32.TryParse("1 - 2i", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(1, z.Real);
            Assert.AreEqual(-2, z.Imaginary);

            ret = Complex32.TryParse("1,2", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(1, z.Real);
            Assert.AreEqual(2, z.Imaginary);

            ret = Complex32.TryParse("1 , 2 ", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(1, z.Real);
            Assert.AreEqual(2, z.Imaginary);

            ret = Complex32.TryParse("1,2i", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(1, z.Real);
            Assert.AreEqual(2, z.Imaginary);

            ret = Complex32.TryParse("-1, -2i", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(-1, z.Real);
            Assert.AreEqual(-2, z.Imaginary);

            ret = Complex32.TryParse("(+1,2i)", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(1, z.Real);
            Assert.AreEqual(2, z.Imaginary);

            ret = Complex32.TryParse("(-1 , -2)", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(-1, z.Real);
            Assert.AreEqual(-2, z.Imaginary);

            ret = Complex32.TryParse("(-1 , -2i)", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(-1, z.Real);
            Assert.AreEqual(-2, z.Imaginary);

            ret = Complex32.TryParse("(+1e1 , -2e-2i)", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(10, z.Real);
            Assert.AreEqual(-.02f, z.Imaginary);

            ret = Complex32.TryParse("(-1E1 -2e2i)", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(-10, z.Real);
            Assert.AreEqual(-200, z.Imaginary);

            ret = Complex32.TryParse("(-1e+1 -2e2i)", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(-10, z.Real);
            Assert.AreEqual(-200, z.Imaginary);

            ret = Complex32.TryParse("(-1e1 -2e+2i)", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(-10, z.Real);
            Assert.AreEqual(-200, z.Imaginary);

            ret = Complex32.TryParse("(-1e-1  -2E2i)", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(-.1f, z.Real);
            Assert.AreEqual(-200, z.Imaginary);

            ret = Complex32.TryParse("(-1e1  -2e-2i)", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(-10, z.Real);
            Assert.AreEqual(-.02f, z.Imaginary);

            ret = Complex32.TryParse("(-1E+1 -2e+2i)", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(-10, z.Real);
            Assert.AreEqual(-200, z.Imaginary);

            ret = Complex32.TryParse("(-1e-1,-2e-2i)", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(-.1f, z.Real);
            Assert.AreEqual(-.02f, z.Imaginary);

            ret = Complex32.TryParse("(+1 +2i)", out z);
            Assert.IsTrue(ret);
            Assert.AreEqual(1, z.Real);
            Assert.AreEqual(2, z.Imaginary);
        }

        [Test]
        public void UnaryPlusTest()
        {
            Complex32 a = new Complex32(1, 1);
            Assert.AreEqual(new Complex32(1, 1), +a);
        }

        [Test]
        public void InfiniteNaNTest()
        {
            Complex32 a = new Complex32(float.PositiveInfinity, float.NaN);
            Complex32 b = new Complex32(float.NaN, float.PositiveInfinity);

            Assert.IsTrue(Complex32.IsNaN(a));
            Assert.IsTrue(Complex32.IsNaN(b));
            Assert.IsFalse(Complex32.IsInfinity(a));
            Assert.IsFalse(Complex32.IsInfinity(b));
        }
    }
}
